﻿/// -----------------------------------------------------------------------
/// (c) 2009 Microsoft Corporation -- All rights reserved
/// This code is licensed under the MS-PL
/// http://www.opensource.org/licenses/ms-pl.html
/// Courtesy of the Open Source Techology Center: http://port25.technet.com
/// -----------------------------------------------------------------------

using System;
using System.IO;
using System.Linq;
using gsToolkit;

namespace SynthFastCGI {
    class Program {
        static string help = @"
Usage:
-------

SynthFastCGI [options]

    options:
    --------
    --help                      this help
    --nologo                    don't display the logo
    --load-config=<configfile>  loads options from configuration file
                                List the options, one per line, without the
                                prefix '--'.

    --fcgi=<cgi-pgm.exe>        REQUIRED: path to the fastcgi handler program


    --output=<filename>        *write output to filename. Filename can contain
                                the following replaceable strings:
                                    {{filename}}  - substitutes for the original
                                                  filename, no extension
                                    {{ext}}         original extension
                                    {{folder}}    - original folder
                                    {{subfolder}} - original folder without leading /
                                    {{count}}     - a running count of the files
                                                  downloaded.
                                    {{date}}      - the current date (y-m-d)
                                    {{date-long}} - the date in long format
                                    {{time}}      - the current time (Hh:mm:ss)
                                    {{time-long}} - the current time in long fmt
                                    {{ticks}}     - the current timestamp as tics

                                Use --output=null to just drop the responses.

    --output-headers=<filename> write headers to filename (see above)

    --run-after=<cmd>           a command to run before stopping fcgi exe
    --run-before=<cmd>          a command to run before starting fcgi exe



    Script Mode:
    ------------
       --script=<scriptFile>    path to script file to pass to CGI Handler
       --iterations=<number>    number of iterations to call the CGI Program

    Request Mode:
    -------------
       --document-root=<path>   sets the virtual root that contains the web
       --request=<url>          requests a URL (strips off the scheme://host)
       --load-requests=<file>   loads the requests from a text file one URL
                                per line.
       --cookie-data=<file>     loads cookie data to file
       --save-cookies           saves the cookies back to the cookie-data file
       --extensions=[.ext,]     a comma seperated list of extensions that
                                actually get processed by the cgi handler.
                                Defaults to .php

    Notes:
    ------
        * any text intended to be filenames are cleaned to eliminate text
          characters that are filesystem unfriendly.

        - Anything Not handled by a parameter listed above is translated into
          a server parameter and passed thru to the CGI Handler:

            --<PARAM>=<value>   add a parameter to pass to the CGI Handler
                                this will up-case the entire PARAM name.

";

        private string not_implemented_yet = @"
        --form-data=[pg]key=value   fills in form fields:
                                    pg - an URL mask (RexEx ok)
                                    key - form field name
                                    value - value to enter
";

        private static bool failTripped = false;

        static int Main(string[] args) {
            return new Program().main(args);
        }

        int main(string[] args) {
            Server server = new Server();

            #region Options and Parameters
            foreach(var arg in args.Switches().Keys) {
                var parameters = args.Switches()[arg];

                switch(arg) {
                    /* options  */
                    case "output":
                        server.OutputFilename = parameters.Last();
                        break;

                    case "output-headers":
                        server.OutputHeadersFilename = parameters.Last();
                        break;


                    case "fcgi":
                        server.CgiExe= Path.GetFullPath(parameters.Last()).ToLower();
                        if(!File.Exists(server.CgiExe)) {
                            return Fail("Unable to find CGI Handler :[{0}]", server.CgiExe);
                        }
                        break;

                    /* global switches */
                    case "load-config":
                        // all ready done, but don't get too picky.
                        break;

                    case "nologo":
                        server.Assembly().SetLogo("");
                        break;

                    case "help":
                        Logo();
                        return Help();

                    /* Script Mode */
                    case "script":
                        server.CgiScript = parameters.Last();
                        var path = Path.GetFullPath(server.CgiScript);
                        server.CgiParameters["SCRIPT_NAME"]= "/"+path.Substring(path.LastIndexOf('\\')+1);
                        server.CgiParameters["PATH_TRANSLATED"]= path;
                        if( !server.CgiParameters.Keys.Contains("URL") )
                            server.CgiParameters["URL"] = "/"+path.Substring(path.LastIndexOf('\\')+1);
                        break;

                    case "iterations":
                        server.Iterations = parameters.Last().ToInt32(1);
                        break;

                    case "run-before":
                        server.Before = parameters.Last();
                        break;

                    case "run-after":
                        server.After = parameters.Last();
                        break;

                    /* Request Mode */
                    case "document-root":
                        server.DocumentRoot = parameters.Last();
                        break;

                    case "request":
                        server.Requests.AddRange(parameters);
                        break;

                    case "load-requests":
                        var rfilename = Path.GetFullPath(parameters.Last());
                        if(!File.Exists(rfilename))
                            return Fail("Unable to find request file [{0}]", rfilename);
                        var reqs = File.ReadAllLines(rfilename);
                        server.Requests.AddRange(reqs);
                        break;

                    case "cookie-data":
                        server.CookieFile = Path.GetFullPath(parameters.Last());
                        break;

                    case "save-cookies":
                        server.SaveCookies = true;
                        break;

                    case "extensions":
                        server.Extensions =parameters;
                        break;

                    default:
                        server.CgiParameters[arg.ToUpper()] = parameters.Last();
                        break;
                }
            }

            if( string.IsNullOrEmpty(server.CgiExe)) {
                Logo();
                return Fail("No CGI Handler specified\r\n\r\n    Use --help for command line help.");
            }
            #endregion

            Logo();
            return server.Run();
        }

        #region fail/help/logo
        public static int Fail(string text, params object[] par) {
            failTripped = true;
            using(new ConsoleColors(ConsoleColor.Red, ConsoleColor.Black))
                Console.WriteLine("Error:{0}", text.format(par));
            return 1;
        }

        int Help() {
            using(new ConsoleColors(ConsoleColor.White, ConsoleColor.Black))
                help.Print();
            return 0;
        }

        void Logo() {
            using(new ConsoleColors(ConsoleColor.Cyan, ConsoleColor.Black))
                this.Assembly().Logo().Print();
            this.Assembly().SetLogo("");
        }
        #endregion
    }
}
